
#import "Common.h"

#import "Camera.h"

@interface Camera ()

-(void) notifyRenderer;

@end


@implementation Camera

@synthesize	projection;

@synthesize	fov;
@synthesize frustum;

@dynamic pos;

@synthesize rightVector,forwardVector,upVector;

@dynamic viewPort;
@synthesize renderer;

@synthesize	globalUpVector;

-(id) init
{
	return [self initWithPerspectiveFov:3.14f/4.0f aspectRatio:320.0f/480.0f zNear:0.1f zFar:1000.0f];
}

-(id) initWithPerspectiveFov:(float)_fov aspectRatio:(float)aspectWdivH zNear:(float)zN zFar:(float)zF
{
	projection = ProjectionPerspective;

	fov = _fov;
	
	frustum.zNear	= zN;
	frustum.zFar	= zF;
	
	frustum.left	= -zN * tanf(fov/2.0f);
	frustum.right	=  zN * tanf(fov/2.0f);
	
	frustum.top		=  frustum.right / aspectWdivH;
	frustum.bottom	= -frustum.right / aspectWdivH;

	globalUpVector = *vecUnitNegZ;
	
//	[self setPosX:0 Y:100 Z:-100];
//	[self lookAtPosX:0.0f Y:0.0f Z:0.0f];
//
//	[self setViewPortX:0 Y:0 W:480.0f H:300.0f];
	
	return self;
}

/*
 *	Оси коорлинат: X - вправо, Y - вниз, Z - от нас.
 */
-(id) initWithOrthoLeft:(float)left Right:(float)right Top:(float)top Bottom:(float)bottom
{
	if(self = [super init])
	{		
		projection = ProjectionOrtho;

		frustum.left = left;
		frustum.right = right;
		frustum.top = top;
		frustum.bottom = bottom;
		frustum.zNear = -50.0f;
		frustum.zFar = 50.0f;
		
		globalUpVector = *vecUnitNegY;

		[self setViewPortX:0 Y:0 W:320.0f H:480.0f];
		
		[self setPosX:0 Y:0 Z:-1];
		[self lookAtDirX:0.0f Y:0.0f Z:1.0f];
	}
	return self;
}

-(void) setFov:(float)_fov
{
	fov = _fov;
		
	frustum.left	= -frustum.zNear * tanf(fov/2.0f);
	frustum.right	=  frustum.zNear * tanf(fov/2.0f);
	
	[self notifyRenderer];
}

-(void) setAspectRatio:(float)aspectWdivH
{
	frustum.top		=  frustum.right / aspectWdivH;
	frustum.bottom	= -frustum.right / aspectWdivH;
	
	[self notifyRenderer];
}

-(void)setViewPortX:(float)x Y:(float)y W:(float)width H:(float)height
{
	viewPort.origin.x = x;
	viewPort.origin.y = y;
	
	viewPort.size.width = width;
	viewPort.size.height = height;
	
	[self notifyRenderer];
}

-(void) notifyRenderer
{
	if(renderer)
		[renderer onCameraChange];
}

-(void) lookAtPos:(const Vector3*)posToLook
{
	vec3Sub(posToLook,&pos,&forwardVector);
	vec3Norm1(&forwardVector);

	//vec3Cross(&forwardVector,vecUnitY, &rightVector);
	vec3Cross(&forwardVector,&globalUpVector, &rightVector);
	vec3Cross(&rightVector,&forwardVector, &upVector);
	
//	vec3Norm1(&forwardVector);
	vec3Norm1(&upVector);
	vec3Norm1(&rightVector);
	
	[self notifyRenderer];
}

-(void) lookAtPosX:(float)x Y:(float)y Z:(float)z
{
	Vector3 _pos = {x,y,z};
	
	[self lookAtPos:&_pos];
}

-(void) setLookDir:(Vector3*)lookDir
{
	Vector3 pointToLook;
	
	vec3Add(&pos,lookDir,&pointToLook);
	
	[self lookAtPos:&pointToLook];
}

-(void) lookAtDirX:(float)x Y:(float)y Z:(float)z
{
	Vector3 dir = {x,y,z};
	
	[self lookAtDir:&dir];
}

-(Vector3) pos
{
	return pos;
}

-(void) setPos:(Vector3)newPos
{
	pos = newPos;
	
	[self notifyRenderer];
}

-(void) setPosX:(float)x Y:(float)y Z:(float)z
{
	pos.x = x;
	pos.y = y;
	pos.z = z;
	
	[self notifyRenderer];
}

-(void) lookAtDir:(Vector3*)lookDir
{
	Vector3 lookPoint;
	
	vec3Add(&pos,lookDir,&lookPoint);
	
	[self lookAtPos:&lookPoint];
}

-(void) setViewPort:(CGRect) newViewPort
{
	viewPort = newViewPort;
	[self notifyRenderer];
}

-(CGRect)viewPort
{
	return viewPort;
}

-(void) getPickRayWithScreenX:(float)sx ScreenY:(float)sy RayOrigin:(Vector3*)rayOrig RayDir:(Vector3*)rayDir
{
	float viewPortLeft	= viewPort.origin.x;
	float viewPortRight = viewPort.origin.x + viewPort.size.width;
	float viewPortTop	= viewPort.origin.y;
	float viewPortBottom= viewPort.origin.y + viewPort.size.height;
	
	float x = (frustum.right - frustum.left) * (sx - viewPortLeft) / (viewPortRight - viewPortLeft) + frustum.left;
	float y = (frustum.bottom - frustum.top) * (sy - viewPortTop) / (viewPortBottom - viewPortTop) + frustum.top;
	
	Vector3 A;
	vec3MulFloat(&rightVector,x,&A);

	Vector3 B;
	vec3MulFloat(&upVector,y,&B);
	
	Vector3 C;
	vec3Add(&A,&B,&C);
	
	Vector3 D;
	vec3MulFloat(&forwardVector,frustum.zNear,&D);
	
	Vector3 E;
	vec3Add(&C,&D,&E);

//	Vector3 G;
//	vec3Add(&E,&pos,&G);
	
	vec3Norm1(&E);	// Ray Dir
	
	*rayOrig = pos;
	*rayDir = E;
}

@end
